package org.aeon.gamechatnest.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.aeon.gamechatnest.common.properties.JwtProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.time.Instant;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

/**
 * @author: MissingAeon
 * @date: 2024/5/9 13:36
 * @description: JWT Token工具类，用于生成和解析JWT Token
 */
@Slf4j
@Component
public class JwtUtil {

    private static SecureDigestAlgorithm<? super Key, ?> algorithm;
    private static SecretKey secretKey;

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 生成访问令牌
     *
     * @param username 用户名
     * @return 访问令牌
     */
    public String genAccessToken(String username) {
        // 生成令牌ID
        String uuid = UUID.randomUUID().toString();
        // 设置过期时间
        Date expireDate = Date.from(Instant.now().plusSeconds(jwtProperties.access_expire));

        return Jwts.builder()
                // 设置头部信息
                .header()
                .add("typ", "JWT")
                .add("alg", jwtProperties.getAlgorithm())
                .and()
                // 设置自定义负载信息
                .claim("username", username)
                .id(uuid) // 令牌ID
                .expiration(expireDate) // 过期日期
                .issuedAt(new Date()) // 签发时间
                .subject(jwtProperties.getSubject()) // 主题
                .issuer(jwtProperties.getJwt_iss()) // 签发者
                .signWith(getSecret(), getAlgorithm()) // 签名
                .compact();
    }

    /**
     * 获取payload中的用户信息
     *
     * @param token JWT Token
     * @return 用户信息
     */
    public String getUserFromToken(String token) {
        String user = "";
        Claims claims = null;
        try {
            String[] split = token.split(" ", 2);
            if (Objects.equals(split[0], "Bearer"))
                claims = parseClaims(split[1]);
        } catch (IndexOutOfBoundsException | NullPointerException ignored) {
        }
        if (claims != null) {
            user = (String) claims.get("username");
        } else {
            log.error("JWT Token中的用户信息获取失败");
        }
        return user;
    }

    /**
     * 获取JWT令牌的过期时间
     *
     * @param token JWT令牌
     * @return 过期时间的毫秒级时间戳
     */
    public long getExpirationTime(String token) {

        Claims claims = parseClaims(token);
        if (claims != null) {
            return claims.getExpiration().getTime();
        }
        log.error("JWT令牌过期时间获取失败");
        return 0L;
    }

    /**
     * 解析token
     *
     * @param token token
     * @return Jws<Claims>
     */
    public Jws<Claims> parseClaim(String token) {
        return Jwts.parser()
                .verifyWith(getSecret())
                .build()
                .parseSignedClaims(token);
    }

    /**
     * 解析JWT Token中的Claims
     *
     * @param token JWT Token
     * @return Claims
     */
    public Claims parseClaims(String token) {
        try {
            return parseClaim(token)
                    .getPayload();
        } catch (Exception e) {
            log.error("JWT Token中的Claims解析失败", e);
            return null;
        }
    }

    /**
     * 解析token的头部信息
     *
     * @param token token
     * @return token的头部信息
     */
    public JwsHeader parseHeader(String token) {
        return parseClaim(token).getHeader();
    }

    /**
     * 解析token的载荷信息
     *
     * @param token token
     * @return token的载荷信息
     */
    public Claims parsePayload(String token) {
        return parseClaim(token).getPayload();
    }

    @SuppressWarnings("unchecked")
    private SecureDigestAlgorithm<? super Key, ?> getAlgorithm() {
        if (algorithm == null) {
            algorithm = (SecureDigestAlgorithm<? super Key, ?>) Jwts.SIG.get().forKey(jwtProperties.getAlgorithm());
        }
        return algorithm;
    }

    private SecretKey getSecret() {
        if (secretKey == null) {
            secretKey = Keys.hmacShaKeyFor(jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8));
        }
        return secretKey;
    }
}

